package com.example.widget_lib.widget.view;

import com.example.widget_lib.callback.Rotatable;
import com.example.widget_lib.callback.SettingsSystemObserver;
import com.example.widget_lib.utils.LogUtil;
import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.IDataAbilityObserver;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Rect;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.RotationEvent;
import ohos.sysappcomponents.settings.SystemSettings;

import java.lang.ref.WeakReference;

public class CircleButton extends Image implements Rotatable, Component.DrawTask,Component.RotationEventListener, SettingsSystemObserver.Callback{
    private static final String TAG = "CircleButton";
    private ShapeElement mNormal;
    private ShapeElement mPressed;
    private ShapeElement mSelected;
    private ShapeElement mDisabled;

    private PixelMap mPixelMap;

    private static int[] STATE_SET_DISABLED = new int[]{0};
    private static int[] STATE_SET_PRESSED = new int[]{1};
    private static int[] STATE_SET_SELECTED = new int[]{2};
    private static int[] STATE_SET_NONE = new int[]{};

    private boolean mEnableAnimation = true;
    private boolean mClockwise = false;

    private static final int ANIMATION_SPEED = 270; // 270 deg/sec

    private float mCurrentDegree = 0f; // [0, 359]
    private float mStartDegree = 0f;
    private float mTargetDegree = 0f;

    private float mAnimationStartTime = 0f;
    private float mAnimationEndTime = 0f;



    private Context mContext;
    private WeakReference<Callback> mCallback;
    public int mOrientation;
    private boolean mOrientationChanging = false;
    private String mAccelerometerRotation;
    private int mOrientationToChange;
    private SettingsSystemObserver mAccelerometerRotationObserver;

    private static final int ORIENTATION_CHANG = 1;
    private static final int ORIENTATION_CHANGED = 2;


    public interface Callback {
        void orientationChanged(int orientation, int previousOrientation);
    }



    public CircleButton(Context context) {
        super(context);
        mContext = context;
        init();
        addDrawTask(this);
    }


    public CircleButton(Context context, AttrSet attrSet) {
        super(context, attrSet);
        mContext = context;
        init();
        addDrawTask(this);
        setRotationEventListener(this);
    }

    @Override
    public void addDrawTask(DrawTask task) {
        super.addDrawTask(task);
        task.onDraw(this, mCanvasForTaskOverContent);
    }

    public void setStroke(int width, RgbColor color) {
        mNormal.setStroke(width, color);
        mPressed.setStroke(width, color);
        mSelected.setStroke(width, color);
    }

    public void setBackgroundNormalColor(RgbColor color) {
        mNormal.setRgbColor(color);
    }

    public void setBackgroundPressedColor(RgbColor color) {
        mPressed.setRgbColor(color);
    }

    public void setBackgroundSelectedColor(RgbColor color) {
        mSelected.setRgbColor(color);
    }

    public void setBackgroundDisabledColor(RgbColor color) {
        mDisabled.setRgbColor(color);
    }

    public void setDisabledStroke(int width, RgbColor color) {
        mDisabled.setStroke(width, color);
    }

    public float getDegree() {
        return mTargetDegree;
    }

    private void init() {
        mNormal = new ShapeElement();
        mNormal.setShape(ShapeElement.OVAL);

        mPressed = new ShapeElement();
        mPressed.setShape(ShapeElement.OVAL);

        mSelected = new ShapeElement();
        mSelected.setShape(ShapeElement.OVAL);

        mDisabled = new ShapeElement();
        mDisabled.setShape(ShapeElement.OVAL);

        StateElement background = new StateElement();
        background.addState(STATE_SET_DISABLED, mDisabled);
        background.addState(STATE_SET_PRESSED, mPressed);
        background.addState(STATE_SET_SELECTED, mSelected);
        background.addState(STATE_SET_NONE, mNormal);
//        setBackgroundDrawable(background);
        setBackground(background);

        mAccelerometerRotationObserver = new SettingsSystemObserver(sHandler);
        mAccelerometerRotationObserver.setCallback(this);

        LogUtil.loge(TAG, "init....");
    }

    @Override
    public void setOrientation(int orientation, boolean animation) {
        LogUtil.loge(TAG, "setOrientation degree...." + orientation);
        mEnableAnimation = animation;
        // make sure in the range of [0, 359]
        orientation = orientation >= 0 ? orientation % 360 : orientation % 360 + 360;
        if (orientation == mTargetDegree) {
            return;
        }
        mTargetDegree = orientation;
        if (mEnableAnimation) {
            mStartDegree = mCurrentDegree;
            mAnimationStartTime = 7371342;//AnimationUtils.currentAnimationTimeMillis();

            double diff = mTargetDegree - mCurrentDegree;
            boolean clockwise = diff == 180;
            diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359]

            // Make it in range [-179, 180]. That's the shorted distance between the two angles
            diff = diff > 180 ? diff - 360 : diff;

            mClockwise = diff >= 0 && diff < 180 || clockwise;
            mAnimationEndTime = (float) (mAnimationStartTime + Math.abs(diff) * 1000 / ANIMATION_SPEED);

        } else {
            mCurrentDegree = mTargetDegree;
        }
        invalidate();

    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        LogUtil.loge(TAG, "onDraw start....");


        Element startElement= getImageElement();
        if (startElement == null) {
            return;
        }
        LogUtil.loge(TAG, "........");
        Rect bounds = startElement.getBounds();
        int w = bounds.right - bounds.left;
        int h = bounds.bottom - bounds.top;

        if (w == 0 || h == 0) {
            return; // nothing to draw
        }

        if (mCurrentDegree != mTargetDegree) {
            long time = 7408105;//AnimationUtils.currentAnimationTimeMillis();
            if (time < mAnimationEndTime) {
                double deltaTime = (int) (time - mAnimationStartTime);
                double degree = mStartDegree + ANIMATION_SPEED * (mClockwise ? deltaTime : -deltaTime) / 1000;
                degree = degree >= 0 ? degree % 360 : degree % 360 + 360;
                mCurrentDegree = (float) degree;
                invalidate();
            } else {
                mCurrentDegree = mTargetDegree;
            }
        }

        int left = getPaddingLeft();
        int top = getPaddingTop();
        int right = getPaddingRight();
        int bottom = getPaddingBottom();
        int width = getWidth() - left - right;
        int height = getHeight() - top - bottom;

        int saveCount = canvas.getSaveCount();

        // Scale down the image first if required.
        if ((getScaleMode() == ScaleMode.ZOOM_CENTER) && ((width < w) || (height < h))) {
            float ratio = Math.min((float) width / w, (float) height / h);
            canvas.scale(ratio, ratio, width / 2.f, height / 2.f);
        }
        canvas.translate(left + width / 2, top + height / 2);
        canvas.rotate(-mCurrentDegree, 0f, 0f);
        canvas.translate(-w / 2, -h / 2);
        startElement.drawToCanvas(canvas);

        canvas.restoreToCount(saveCount);

        LogUtil.loge(TAG, "onDraw end....");
    }


    @Override
    public boolean onRotationEvent(Component component, RotationEvent rotationEvent) {
        float orientation = rotationEvent.getRotationValue();
        LogUtil.loge("旋转", "orientation = " + orientation + "->mOrientationChanging = " + mOrientationChanging);

//        if (mAccelerometerRotation == 0) return;

        Callback callback = getCallback();
        if (callback == null) {
            return false;
        }
        int newOrientation = 0;//Surface.ROTATION_0;
        if (orientation >= 45 && orientation < 135) {
            newOrientation = 90;//Surface.ROTATION_90;
        } else if (orientation >= 135 && orientation < 225) {
            newOrientation = 180;//Surface.ROTATION_180;
        } else if (orientation >= 225 && orientation < 315) {
            newOrientation =270;// Surface.ROTATION_270;
        }

        if (mOrientationChanging) { // 旋转中
            if (newOrientation != mOrientationToChange) {
                sHandler.removeEvent(ORIENTATION_CHANGED);
                sHandler.removeEvent(ORIENTATION_CHANG);
                mOrientationToChange = newOrientation;

                InnerEvent innerEvent = InnerEvent.get();
                innerEvent.eventId = ORIENTATION_CHANG;
                innerEvent.object = this;
                innerEvent.param=newOrientation;
                sHandler.sendEvent(innerEvent,500);
            }
        } else {
            setOrientation(newOrientation);
        }
        return false;
    }
    private void setOrientation(int orientation) {
        if (mOrientation != orientation) {
            int previousOrientation = mOrientation;
            mOrientation = orientation;
            Callback callback = getCallback();
            if (callback != null) {
                callback.orientationChanged(orientation, previousOrientation);
            }
            mOrientationChanging = true;
            mOrientationToChange = orientation;
            InnerEvent innerEvent = InnerEvent.get();
            innerEvent.eventId = ORIENTATION_CHANGED;
            innerEvent.object = this;
            sHandler.sendEvent(innerEvent,500);
        } else {
            mOrientationChanging = false;
        }
    }
    public void setCallback(Callback callback) {
        mCallback = (callback == null) ? null : new WeakReference<>(callback);
    }

    public Callback getCallback() {
        return (mCallback == null) ? null : mCallback.get();
    }

    @Override
    public void mtcSettingsSystemChanged(SettingsSystemObserver observer) {
        DataAbilityHelper creator = DataAbilityHelper.creator(mContext);
        try {
            mAccelerometerRotation = SystemSettings.getValue(creator, SystemSettings.General.ACCELEROMETER_ROTATION_STATUS);
            LogUtil.loge("旋转", "mtcSettingsSystemChanged...");
        } catch (Exception e) {
            mAccelerometerRotation = null;
            e.printStackTrace();
        }
    }

    public void enable() {
        mOrientation = 0;//((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();

        DataAbilityHelper creator = DataAbilityHelper.creator(mContext);
        try {
            mAccelerometerRotation = SystemSettings.getValue(creator, SystemSettings.General.ACCELEROMETER_ROTATION_STATUS);
        } catch (Exception e) {
            mAccelerometerRotation = null;
            e.printStackTrace();
        }
        mAccelerometerRotationObserver.register(creator,SystemSettings.General.ACCELEROMETER_ROTATION_STATUS);

    }

    private void orientationChanged() {
        mOrientationChanging = false;
    }

    private static EventHandler sHandler = new  EventHandler(EventRunner.create()){
        @Override
        public void sendEvent(InnerEvent event) throws IllegalArgumentException {
            super.sendEvent(event);
            switch (event.eventId){
                case ORIENTATION_CHANG:
                    CircleButton listener = (CircleButton) event.object;
                    listener.setOrientation((int)event.param);
                    break;
                case ORIENTATION_CHANGED:
                    CircleButton listener1 = (CircleButton) event.object;
                    listener1.orientationChanged();
                    break;
            }
        }
    };

}
